package com.simple.simple_view.utils;

import javax.annotation.Resource;

import com.alibaba.fastjson.JSONObject;
import com.simple.simple_view.domain.entity.Video;
import com.simple.simple_view.domain.system.RedisSystemParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
@SuppressWarnings({"unchecked","rawtypes"})
public class RedisCache
{
    @Resource
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }else{
            //TODO 数据库不存在此数据
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获得缓存的map并转换为java对象
     *
     * @param key
     * @return
     */
    public <T> T getMapToObject(final String key,Class<T> targetType)
    {
        JSONObject jsonObject = new JSONObject(redisTemplate.opsForHash().entries(key));

        return jsonObject.toJavaObject(targetType);
    }
    /**
     * 获得缓存的多个map并转换为java对象
     *
     * @param keys
     * @return
     */
    public <T> List<T> getObjectList(final List<String> keys)
    {
        List<T> list = new ArrayList<>();
        Objects.requireNonNull(redisTemplate.opsForValue().multiGet(keys)).forEach(value -> list.add((T)value));
        return list;
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }

    /**
     * 对redis中某key的map值中的HKey递增v
     * @param key redis中的key
     * @param HKey Map中的key
     * @param v 递增值
     */
    public void incrementCacheMapValue(String key, String HKey,int v) {
        redisTemplate.opsForHash().increment(key,HKey,v);
    }

    /**
     *
     * @param key redis中的key
     * @param object 对象
     * @param <T> 对象类型
     */
    public <T> void saveObjectToHash(final String key,final T object,final Integer timeout, final TimeUnit timeUnit) {
        // 获取对象的字段和字段值
        Field[] fields = object.getClass().getDeclaredFields();
        Map<String, Object> fieldMap = new HashMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(object);
                if (value != null) {
                    fieldMap.put(field.getName(), value);
                }
            } catch (IllegalAccessException e) {
                log.error(e.getMessage(), e);
            }
        }

        // 使用 HashOperations 存储字段和字段值
        if (!fieldMap.isEmpty()) {
            HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
            hashOps.putAll(key, fieldMap);
            redisTemplate.expire(key, timeout, timeUnit);
        }
    }

    public<T> void addToZSet(String key, T object, int score) {
        redisTemplate.opsForZSet().add(key, object, score);
    }
    // 自增ZSET中指定成员的分数
    public void incrementScore(String key, String member, double delta) {
        redisTemplate.opsForZSet().incrementScore(key, member, delta);
    }
    public <T>Set<T> opsForZSet(String key, int start, int end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }
    public List<String> getHotVideo(int start, int end, String type) {
        List<String> hotWordList = new ArrayList<>();
        Set<ZSetOperations.TypedTuple<String>> typedTupleSet = redisTemplate.opsForZSet().reverseRangeWithScores(type,start,end);
        if (typedTupleSet != null) {
            typedTupleSet.forEach(item ->
                    hotWordList.add(item.getValue()));
        }
        return hotWordList;
    }

    public void batchDeleteObject(List<Long> videoIdSet) {
        videoIdSet.forEach(o->deleteObject(RedisSystemParam.VIDEO_PREFIX+o.toString()));
    }
    @Async
    public<T> void batchSetObject(List<Video> records) {
        records.forEach(video->{
            setCacheObject(RedisSystemParam.VIDEO_PREFIX+video.getId(),
                    video,
                    RedisSystemParam.VIDEO_EXPIRES+new Random().nextInt(RedisSystemParam.VIDEO_EXPIRES),
                    TimeUnit.SECONDS);
        });
    }
    @Async
    public void batchSaveObjectToHash(List<Video> records) {
        records.forEach(video -> {
            saveObjectToHash(RedisSystemParam.VIDEO_PREFIX+video.getId(),
                    video,
                    RedisSystemParam.VIDEO_EXPIRES+new Random().nextInt(RedisSystemParam.VIDEO_EXPIRES),
                    TimeUnit.SECONDS);
        });
    }
}